/*
 *  Copyright 2019-2020 Zheng Jie
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.js.data.system.service.impl;


import cn.hutool.core.lang.Dict;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.js.data.common.constant.CommonConstant;
import com.js.data.common.constant.SymbolConstant;
import com.js.data.common.exception.ApiException;
import com.js.data.common.query.BaseQueryInfo;
import com.js.data.common.utils.StringUtils;
import com.js.data.system.annotation.Log;
import com.js.data.system.entity.SysLog;
import com.js.data.system.mapper.SysLogMapper;
import com.js.data.system.service.LogService;
import com.js.data.system.vo.LogPageVO;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestBody;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.time.LocalDateTime;
import java.util.*;


@Service
public class LogServiceImpl extends ServiceImpl<SysLogMapper, SysLog> implements LogService {

    /**
     * 保存日志信息
     *
     * @param joinPoint 连接点
     * @param log       log信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveLog(ProceedingJoinPoint joinPoint, SysLog log) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        Log aopLog = method.getAnnotation(Log.class);
        // 方法路径
        String methodName = joinPoint.getTarget().getClass().getName()
                + SymbolConstant.DOT + signature.getName() + SymbolConstant.PARENTHESES;
        log.setMethod(methodName);
        // 描述
        log.setDescription(aopLog.value());
        String param = getParameter(method, joinPoint.getArgs());
        log.setParams(param!=null&&param.length()>2000?"":param);
        log.setAddress(StringUtils.getCityInfo(log.getRequestIp()));
        // 记录登录用户，隐藏密码信息
        if (signature.getName().equals(CommonConstant.LOGIN_STR) && StringUtils.isNotEmpty(log.getParams())) {
            JSONObject obj = JSONUtil.parseObj(log.getParams());
            log.setUsername(obj.getStr(CommonConstant.USER_NAME_STR, SymbolConstant.BLANK));
            log.setParams(JSONUtil.toJsonStr(Dict.create().set(CommonConstant.USER_NAME_STR, log.getUsername())));
        }
        //如果是记录业务信息的，用户名记录设备编号
        if(methodName.contains("InItemController.saveLog()")){
            JSONArray array = JSONUtil.parseArray(param);
            JSONObject obj = array.size()>1? array.getJSONObject(1):null;
            if(obj != null){
                log.setUsername(obj.getStr("cookie"));
            }
        }

        log.setCreateTime(LocalDateTime.now());
        save(log);
    }

    /**
     * 日志分页
     *
     * @param query 查询条件
     * @return 分页
     */
    @Override
    public IPage<LogPageVO> pageLog(BaseQueryInfo query) {
        Page<LogPageVO> page = new Page<>(query.getCurrent(), query.getSize());
        return getBaseMapper().selectPageVO(page, query);
    }

    /**
     * 获取错误日志详情
     *
     * @param id id
     * @return String
     */
    @Override
    public String errorLogDetail(Long id) {
        SysLog sysLog = getById(id);
        Optional.ofNullable(sysLog).orElseThrow(() -> new ApiException("数据不存在"));
        return new String(sysLog.getExceptionDetail());
    }

    /**
     * 根据方法和传入的参数获取请求参数
     */
    private String getParameter(Method method, Object[] args) {
        List<Object> argList = new ArrayList<>();
        Parameter[] parameters = method.getParameters();
        for (int i = 0; i < parameters.length; i++) {
            //将RequestBody注解修饰的参数作为请求参数
            RequestBody requestBody = parameters[i].getAnnotation(RequestBody.class);
            if (requestBody != null) {
                argList.add(args[i]);
            }else{
                //将RequestParam注解修饰的参数作为请求参数
                Map<String, Object> map = new HashMap<>(2);
                String key = parameters[i].getName();
                map.put(key, args[i]);
                argList.add(map);
            }
        }
        if (argList.isEmpty()) {
            return "";
        }
        return argList.size() == 1 ? JSONUtil.toJsonStr(argList.get(0)) : JSONUtil.toJsonStr(argList);
    }
}
